package com.vtron.utils.io;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @文件读写工具类
 * @date : 2021/12/23 17:34
 * @author: 忻龙彪
 */
public class FileUtils {
    public static final int BUFFER = 1024;//缓存大小

    /**
     * @写文件
     */
    public static void write(String fileName, String content) throws Exception {
        File file = new File(fileName);
        if (!file.exists()) {
            file.getParentFile().mkdirs();
        }
        file.createNewFile();
        // FileWriter fw = new FileWriter(file, true);
        OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write(content);
        bw.flush();
        bw.close();
        fw.close();

    }

    /**
     * @读zip文件
     */
    public static void readZip(String fileName, Predicate<ZipEntry> condition, Consumer<ZipEntry> action) throws IOException {
        readZip(fileName, (zf, ze) -> {
            if (condition.test(ze)) {
                action.accept(ze);
            }
        });
    }

    /**
     * @读zip文件
     */
    public static void readZip(String fileName, BiConsumer<ZipFile, ZipEntry> action) throws IOException {
        ZipFile zf = new ZipFile(fileName);
        InputStream in = new BufferedInputStream(new FileInputStream(fileName));
        ZipInputStream zin = new ZipInputStream(in, Charset.forName("gbk"));
        try {
            ZipEntry ze;
            while ((ze = zin.getNextEntry()) != null) {
                if (action != null) {
                    action.accept(zf, ze);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            zin.closeEntry();
        }
    }

    /**
     * @写zip文件
     */
    public static void writeZip(String srcDir, String zipFileName) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        File srcDirFile = new File(srcDir);
        if (!srcDirFile.isDirectory()) {
            throw new FileNotFoundException(srcDir + " Not a directory");
        }
        File f = new File(zipFileName);
        if (f.exists()) {
            f.delete();
        }
        OutputStream out = new FileOutputStream(new File(zipFileName));
        ZipOutputStream zos = null;
        try {
            zos = new ZipOutputStream(out);
            File sourceFile = new File(srcDir);
            compress(sourceFile, zos, sourceFile.getName(), true);
            long end = System.currentTimeMillis();
            System.out.println(srcDir + "压缩完成，耗时：" + (end - start) + " ms");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (zos != null) {
                try {
                    zos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private static void compress(File sourceFile, ZipOutputStream zos, String name,
                                 boolean KeepDirStructure) throws Exception {
        byte[] buf = new byte[2 * 1024];
        if (sourceFile.isFile()) {
            // 向zip输出流中添加一个zip实体，构造器中name为zip实体的文件的名字
            zos.putNextEntry(new ZipEntry(name));
            // copy文件到zip输出流中
            int len;
            FileInputStream in = new FileInputStream(sourceFile);
            while ((len = in.read(buf)) != -1) {
                zos.write(buf, 0, len);
            }
            // Complete the entry
            zos.closeEntry();
            in.close();
        } else {
            File[] listFiles = sourceFile.listFiles();
            if (listFiles == null || listFiles.length == 0) {
                // 需要保留原来的文件结构时,需要对空文件夹进行处理
                if (KeepDirStructure) {
                    // 空文件夹的处理
                    zos.putNextEntry(new ZipEntry(name + "/"));
                    // 没有文件，不需要文件的copy
                    zos.closeEntry();
                }

            } else {
                for (File file : listFiles) {
                    // 判断是否需要保留原来的文件结构
                    if (KeepDirStructure) {
                        // 注意：file.getName()前面需要带上父文件夹的名字加一斜杠,
                        // 不然最后压缩包中就不能保留原来的文件结构,即：所有文件都跑到压缩包根目录下了
                        compress(file, zos, name + "/" + file.getName(), KeepDirStructure);
                    } else {
                        compress(file, zos, file.getName(), KeepDirStructure);
                    }

                }
            }
        }
    }

    /**
     * zip压缩功能.
     * 压缩baseDir(文件夹目录)下所有文件，包括子目录
     *
     * @throws Exception
     */
    public static void zipFile(String baseDir, String fileName) throws Exception {
        List fileList = getSubFiles(new File(baseDir));
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(fileName));
        ZipEntry ze = null;
        byte[] buf = new byte[1024];
        int readLen = 0;
        for (int i = 0; i < fileList.size(); i++) {
            File f = (File) fileList.get(i);
            ze = new ZipEntry(getAbsFileName(baseDir, f));
            ze.setSize(f.length());
            ze.setTime(f.lastModified());
            zos.putNextEntry(ze);
            InputStream is = new BufferedInputStream(new FileInputStream(f));
            while ((readLen = is.read(buf, 0, BUFFER)) != -1) {
                zos.write(buf, 0, readLen);
            }
            is.close();
        }
        zos.close();
    }

    /**
     * zip压缩功能.
     */
    public static void zipFile(String zipDir) throws Exception {
        File zfile = new File(zipDir);
        if (!zfile.exists()) {
            throw new NullPointerException("The Zip FileDir " + zipDir + " does not exist.");
        }
        zipFile(zipDir, zipDir + ".zip");
    }

    /**
     * 给定根目录，返回另一个文件名的相对路径，用于zip文件中的路径.
     *
     * @param baseDir      java.lang.String 根目录
     * @param realFileName java.io.File 实际的文件名
     * @return 相对文件名
     */
    private static String getAbsFileName(String baseDir, File realFileName) {
        File real = realFileName;
        File base = new File(baseDir);
        String ret = real.getName();
        while (true) {
            real = real.getParentFile();
            if (real == null)
                break;
            if (real.equals(base))
                break;
            else
                ret = real.getName() + "/" + ret;
        }
        return ret;
    }

    /**
     * 取得指定目录下的所有文件列表，包括子目录.
     *
     * @param baseDir File 指定的目录
     * @return 包含java.io.File的List
     */
    private static List getSubFiles(File baseDir) {
        List ret = new ArrayList();
        File[] tmp = baseDir.listFiles();
        for (int i = 0; i < tmp.length; i++) {
            if (tmp[i].isFile())
                ret.add(tmp[i]);
            if (tmp[i].isDirectory())
                ret.addAll(getSubFiles(tmp[i]));
        }
        return ret;
    }

    public static void unZipFile(String zipFile) {
        if (!zipFile.endsWith(".zip")) {
            throw new NullPointerException("The file must be in ZIP format");
        }
        File zfile = new File(zipFile);
        if (!zfile.exists()) {
            throw new NullPointerException("The Zip FileDir " + zipFile + " does not exist.");
        }

        String outputFolder = zipFile.substring(0, zipFile.lastIndexOf(".zip"));
        File unzip = new File(outputFolder);
        if (unzip.exists()) {
            deleteDir(unzip);
        }
        unzip.mkdir();
        unZipFile(zipFile, outputFolder);
    }

    /**
     * 解压
     *
     * @param zipFile
     * @param outputFolder
     */
    public static void unZipFile(String zipFile, String outputFolder) {
        byte[] buffer = new byte[1024];
        try {
            File folder = new File(outputFolder);
            if (!folder.exists())
                folder.mkdir();
            if (folder.exists()) {
                try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) {
                    ZipEntry ze = zis.getNextEntry();
                    while (ze != null) {
                        String fileName = ze.getName();
                        File newFile = new File(outputFolder + File.separator + fileName);
                        if (ze.isDirectory()) {
                            (new File(newFile.getAbsolutePath())).mkdirs();
                        } else {
                            if (!newFile.getParentFile().exists())
                                newFile.getParentFile().mkdirs();
                            try (FileOutputStream fos = new FileOutputStream(newFile)) {
                                int len;
                                while ((len = zis.read(buffer)) > 0)
                                    fos.write(buffer, 0, len);
                            }
                        }
                        ze = zis.getNextEntry();
                    }
                    zis.closeEntry();
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    /**
     * 删除目录
     *
     * @param dir
     * @return
     */
    public static boolean deleteDir(File dir) {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
        // 目录此时为空，可以删除
        return dir.delete();
    }

    /**
     * 创建目录
     * @param dirFullPath 目录路径
     */
    public static void createDir(String dirFullPath) throws IOException {
        createDir(dirFullPath,false);
    }

    /**
     * 创建目录
     * @param dirFullPath 目录路径
     * @param existsDelete 是否删除已存在的目录
     */
    public static void createDir(String dirFullPath,Boolean existsDelete) throws IOException {
        File dir = new File(dirFullPath);
        if (!dir.exists()) {
            dir.mkdirs();
        }else if(existsDelete){
            deleteDir(dir);
            dir.mkdirs();
        }
    }
    /**
     * 复制目录
     */
    public static void copy(String source, String taget) throws IOException {

        File sourceDir = new File(source);
        if (!sourceDir.exists()) {
            throw new IOException("source directory does not exist");
        }
        String tagetDirString = String.format("%s\\%s", taget, sourceDir.getName());
        createDir(tagetDirString);
        File tagetDir = new File(tagetDirString);
        if (sourceDir.isDirectory()) {
            for (File f : sourceDir.listFiles()) {
                if (f.isDirectory()) {
                    copy(f.getPath(), tagetDir.getPath());
                } else {
                    String tagetFilePath = String.format("%s/%s", tagetDirString, f.getName());
                    Files.copy(f.toPath(), Paths.get(tagetFilePath));
                }
            }
        }
    }
}
